package com.portal.mq.service.impl;

import com.portal.mq.mapper.RabbitMapper;
import com.portal.mq.service.RabbitMqService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.UUID;

/**
 * RabbitMQ 服务实现类
 * 提供消息队列的核心功能实现，包括：
 * 1. 队列和交换机的声明与绑定
 * 2. 消息的发送
 * 3. 用户相关操作
 * 4. 邮件发送功能
 */
@Slf4j
@Service
public class RabbitMqServiceImpl implements RabbitMqService {

    /**
     * RabbitMQ数据访问层
     */
    @Resource
    private RabbitMapper rabbitMapper;

    /**
     * Java邮件发送器
     */
    @Resource
    private JavaMailSender jms;

    /**
     * RabbitTemplate: Spring AMQP的核心组件
     * 用于发送和接收消息，提供了同步和异步的消息操作方法
     */
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * AmqpAdmin: AMQP管理组件
     * 用于管理AMQP组件（队列、交换机、绑定等）的声明、删除等操作
     */
    @Resource
    private AmqpAdmin amqpAdmin;

    /**
     * 邮件发送者地址
     */
    @Value("${spring.mail.username}")
    private String sender;

    /**
     * 声明队列和交换机，并建立绑定关系
     * 该方法会执行以下操作：
     * 1. 声明一个持久化的队列
     * 2. 声明一个持久化的直连交换机
     * 3. 将队列和交换机通过路由键绑定
     * 
     * @param queueName 队列名称，同时用作路由键
     * @param exchangeName 交换机名称
     * @throws RuntimeException 当声明过程发生错误时抛出
     */
    @Override
    public void declareQueueAndExchange(String queueName, String exchangeName) {
        try {
            log.info("开始声明队列和交换机 - Queue: {}, Exchange: {}", queueName, exchangeName);
            
            // 1. 声明队列
            // durable=true 表示队列持久化，消息会被存储到磁盘，服务重启后不会丢失
            Queue queue = new Queue(queueName, true);
            amqpAdmin.declareQueue(queue);
            
            // 2. 声明直连交换机
            // durable=true 表示交换机持久化
            // autoDelete=false 表示交换机不会在最后一个队列解绑时自动删除
            DirectExchange exchange = new DirectExchange(exchangeName, true, false);
            amqpAdmin.declareExchange(exchange);
            
            // 3. 建立绑定关系
            // 使用队列名作为路由键，确保消息能准确投递到指定队列
            amqpAdmin.declareBinding(BindingBuilder.bind(queue)
                    .to(exchange)
                    .with(queueName));
            
            log.info("队列和交换机声明完成");
        } catch (Exception e) {
            log.error("队列和交换机声明失败 - Queue: {}, Exchange: {}, Error: {}", 
                    queueName, exchangeName, e.getMessage());
            throw new RuntimeException("队列和交换机声明失败", e);
        }
    }

    /**
     * 发送消息到指定的交换机
     * 该方法会：
     * 1. 验证参数的有效性
     * 2. 检查交换机是否存在
     * 3. 发送持久化消息
     * 4. 记录发送结果
     * 
     * @param exchange 交换机名称
     * @param routingKey 路由键
     * @param message 消息内容
     * @throws IllegalArgumentException 当参数无效时抛出
     * @throws RuntimeException 当消息发送失败时抛出
     */
    @Override
    public void sendMessage(String exchange, String routingKey, Object message) {
        // 参数校验
        if (exchange == null || exchange.trim().isEmpty()) {
            throw new IllegalArgumentException("交换机名称不能为空");
        }
        if (routingKey == null || routingKey.trim().isEmpty()) {
            throw new IllegalArgumentException("路由键不能为空");
        }
        if (message == null) {
            throw new IllegalArgumentException("消息内容不能为空");
        }

        try {
            // 检查交换机是否存在
            if (!exchangeExists(exchange)) {
                log.error("交换机 {} 不存在", exchange);
                throw new RuntimeException("交换机不存在，请先创建交换机");
            }

            // 发送消息
            // MessageDeliveryMode.PERSISTENT 确保消息持久化，防止丢失
            rabbitTemplate.convertAndSend(exchange, routingKey, message, msg -> {
                msg.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                return msg;
            });

            log.info("消息发送成功 - Exchange: {}, RoutingKey: {}, Message: {}", 
                    exchange, routingKey, message);
        } catch (Exception e) {
            log.error("消息发送失败 - Exchange: {}, RoutingKey: {}, Message: {}, Error: {}", 
                    exchange, routingKey, message, e.getMessage(), e);
            throw new RuntimeException("消息发送失败: " + e.getMessage(), e);
        }
    }

    /**
     * 检查交换机是否存在
     * 通过尝试创建临时队列并绑定到目标交换机来检查交换机是否存在
     * 
     * @param exchangeName 要检查的交换机名称
     * @return true 如果交换机存在，false 如果交换机不存在
     */
    private boolean exchangeExists(String exchangeName) {
        try {
            // 创建一个临时队列（自动删除，独占）
            String tempQueueName = "temp." + UUID.randomUUID().toString();
            Queue tempQueue = new Queue(tempQueueName, false, true, true);
            amqpAdmin.declareQueue(tempQueue);
            
            // 尝试将临时队列绑定到目标交换机
            DirectExchange exchange = new DirectExchange(exchangeName);
            amqpAdmin.declareBinding(
                BindingBuilder.bind(tempQueue)
                    .to(exchange)
                    .with("temp.routing.key")
            );
            
            // 清理临时队列
            amqpAdmin.deleteQueue(tempQueueName);
            return true;
        } catch (Exception e) {
            log.warn("检查交换机 {} 是否存在时发生错误: {}", exchangeName, e.getMessage());
            return false;
        }
    }

    /**
     * TODO: 激活用户账号
     * 
     * @param userId 用户ID
     * @return 激活结果，0表示失败，1表示成功
     */
    @Override
    public int activeUser(Long userId) {
        return 0;
    }

    /**
     * TODO: 删除指定用户
     * 
     * @param userId 要删除的用户ID
     * @return 删除结果，0表示失败，1表示成功
     */
    @Override
    public int deleteUserById(Long userId) {
        return 0;
    }

    /**
     * TODO: 发送简单邮件
     * 实现邮件发送功能
     */
    public void sendSimpleMail() {
    }
}